package logging

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"

	"gitee.com/kun-ease/libra/config"
	"github.com/google/uuid"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
)

type Logger struct {
	sugaredLogger *zap.SugaredLogger
	config        config.Log
}

var (
	defaultLogger *Logger
	traceIDKey    = "trace_id"
	traceIDMap    = sync.Map{} // map[goroutineID]string
)

// 使用 sync.Map 存储 goroutine ID 到 traceID 的映射

// getGoroutineID 使用 runtime.Stack 获取 goroutine ID
func getGoroutineID() uint64 {
	buf := make([]byte, 64)
	n := runtime.Stack(buf, false)
	var id uint64
	fmt.Sscanf(string(buf[:n]), "goroutine %d", &id)
	return id
}

// SetTraceID 设置当前 goroutine 的 traceID
func SetTraceID(traceID string) {
	gid := getGoroutineID()
	traceIDMap.Store(gid, traceID)
}

// GetTraceID 获取当前 goroutine 的 traceID
func GetTraceID() string {
	gid := getGoroutineID()
	if traceID, ok := traceIDMap.Load(gid); ok {
		return traceID.(string)
	}
	return ""
}

// ClearTraceID 清除当前 goroutine 的 traceID
func ClearTraceID() {
	gid := getGoroutineID()
	traceIDMap.Delete(gid)
}

// NewTraceID 生成新的 traceID
func NewTraceID() string {
	return uuid.New().String()
}

// WithTraceID 创建新的 traceID 并设置到当前 goroutine
func WithTraceID() string {
	traceID := NewTraceID()
	SetTraceID(traceID)
	return traceID
}

func Init(cfg config.Log) error {
	logger, err := NewLogger(cfg)
	if err != nil {
		fmt.Println("init logger error:", err)
		return err
	}
	defaultLogger = logger
	return nil
}

func NewLogger(cfg config.Log) (*Logger, error) {
	// 创建 zap 配置
	zapConfig := zap.NewProductionEncoderConfig()

	// 自定义时间格式化函数
	zapConfig.EncodeTime = func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
		enc.AppendString(t.Format("2006-01-02 15:04:05.000"))
	}
	zapConfig.EncodeLevel = zapcore.CapitalLevelEncoder

	var cores []zapcore.Core

	// 控制台输出（根据配置决定是否启用）
	if cfg.Cosole {
		consoleEncoder := zapcore.NewConsoleEncoder(zapConfig)
		consoleCore := zapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stdout), getLevel(cfg.Level))
		cores = append(cores, consoleCore)
	}

	// 如果配置了日志路径，则添加文件输出
	if cfg.LogPath != "" {
		// 确保日志目录存在
		if err := os.MkdirAll(cfg.LogPath, 0755); err != nil {
			return nil, fmt.Errorf("failed to create log directory: %v", err)
		}

		// 为启用的日志级别创建对应的文件 core
		enabledLevels := getEnabledLevels(cfg.Level)
		levelNames := map[zapcore.Level]string{
			zapcore.DebugLevel: "debug",
			zapcore.InfoLevel:  "info",
			zapcore.WarnLevel:  "warn",
			zapcore.ErrorLevel: "error",
		}

		for _, level := range enabledLevels {
			levelName := levelNames[level]
			encoder := zapcore.NewJSONEncoder(zapConfig)

			// 创建支持时间轮转的写入器
			writer := getTimeRotatingWriter(cfg.LogPath, levelName, cfg.Rotate)
			core := zapcore.NewCore(encoder, writer, level)
			cores = append(cores, core)
		}
	}

	// 如果没有任何输出目标，创建一个空的 core
	if len(cores) == 0 {
		// 创建一个丢弃所有日志的 core
		nullCore := zapcore.NewNopCore()
		cores = append(cores, nullCore)
	}

	// 合并所有 core
	core := zapcore.NewTee(cores...)

	logger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
	sugaredLogger := logger.Sugar()

	return &Logger{
		sugaredLogger: sugaredLogger,
		config:        cfg,
	}, nil
}

// getEnabledLevels 获取启用的日志级别
func getEnabledLevels(level string) []zapcore.Level {
	minLevel := getLevel(level)
	var levels []zapcore.Level

	allLevels := []zapcore.Level{
		zapcore.DebugLevel,
		zapcore.InfoLevel,
		zapcore.WarnLevel,
		zapcore.ErrorLevel,
	}

	for _, l := range allLevels {
		if l >= minLevel {
			levels = append(levels, l)
		}
	}

	return levels
}

func getLevel(level string) zapcore.Level {
	switch strings.ToLower(level) {
	case "debug":
		return zapcore.DebugLevel
	case "info":
		return zapcore.InfoLevel
	case "warn":
		return zapcore.WarnLevel
	case "error":
		return zapcore.ErrorLevel
	default:
		return zapcore.InfoLevel
	}
}

// TimeRotatingWriter 支持按时间轮转的写入器
type TimeRotatingWriter struct {
	mu             sync.Mutex
	logPath        string
	level          string
	rotate         string
	currentWriter  *lumberjack.Logger
	lastCheckTime  time.Time
	filenameFormat string
}

// getTimeRotatingWriter 获取时间轮转写入器
func getTimeRotatingWriter(logPath, level, rotate string) zapcore.WriteSyncer {
	var filenameFormat string
	if rotate == "hour" {
		filenameFormat = "2006-01-02-15" // 年-月-日-时
	} else {
		filenameFormat = "2006-01-02" // 年-月-日
	}

	writer := &TimeRotatingWriter{
		logPath:        logPath,
		level:          level,
		rotate:         rotate,
		lastCheckTime:  time.Now(),
		filenameFormat: filenameFormat,
	}

	writer.rotateIfNeeded()
	return writer
}

// rotateIfNeeded 检查并执行轮转
func (w *TimeRotatingWriter) rotateIfNeeded() {
	w.mu.Lock()
	defer w.mu.Unlock()

	now := time.Now()
	shouldRotate := false

	// 检查是否需要轮转
	if w.currentWriter == nil {
		shouldRotate = true
	} else {
		lastTime := w.lastCheckTime
		if w.rotate == "hour" {
			if lastTime.Hour() != now.Hour() ||
				lastTime.Day() != now.Day() ||
				lastTime.Month() != now.Month() ||
				lastTime.Year() != now.Year() {
				shouldRotate = true
			}
		} else {
			if lastTime.Day() != now.Day() ||
				lastTime.Month() != now.Month() ||
				lastTime.Year() != now.Year() {
				shouldRotate = true
			}
		}
	}

	if shouldRotate {
		// 生成新的文件名
		filename := filepath.Join(w.logPath, fmt.Sprintf("%s.%s.log", w.level, now.Format(w.filenameFormat)))

		// 关闭旧的写入器
		if w.currentWriter != nil {
			w.currentWriter.Close()
		}

		// 创建新的写入器
		w.currentWriter = &lumberjack.Logger{
			Filename:   filename,
			MaxSize:    100, // MB
			MaxAge:     30,  // 天
			MaxBackups: 10,
			LocalTime:  true,
			Compress:   false,
		}

		w.lastCheckTime = now
	}
}

func (w *TimeRotatingWriter) Write(p []byte) (n int, err error) {
	w.rotateIfNeeded()

	w.mu.Lock()
	defer w.mu.Unlock()

	if w.currentWriter != nil {
		return w.currentWriter.Write(p)
	}
	return len(p), nil
}

func (w *TimeRotatingWriter) Sync() error {
	w.mu.Lock()
	defer w.mu.Unlock()

	// lumberjack 没有 Sync 方法，返回 nil
	return nil
}

// addTraceID 向日志参数中添加 trace_id
func addTraceID(keysAndValues ...interface{}) []interface{} {
	traceID := GetTraceID()
	if traceID == "" {
		traceID = NewTraceID()
		SetTraceID(traceID)
	}

	// 在参数前添加 trace_id
	args := make([]interface{}, 0, len(keysAndValues)+2)
	args = append(args, traceIDKey, traceID)
	args = append(args, keysAndValues...)
	return args
}

// ==================== 全局日志方法（隐式 trace_id）====================

// Debug logs a message at DebugLevel with implicit trace_id
func Debug(msg string, keysAndValues ...interface{}) {
	if defaultLogger != nil {
		args := addTraceID(keysAndValues...)
		defaultLogger.sugaredLogger.Debugw(msg, args...)
	}
}

// Debugf logs a formatted message at DebugLevel with implicit trace_id
func Debugf(template string, args ...interface{}) {
	if defaultLogger != nil {
		msg := fmt.Sprintf(template, args...)
		traceID := GetTraceID()
		if traceID == "" {
			traceID = NewTraceID()
			SetTraceID(traceID)
		}
		defaultLogger.sugaredLogger.Debugw(msg, traceIDKey, traceID)
	}
}

// Debugw logs a message with structured fields at DebugLevel with implicit trace_id
func Debugw(msg string, keysAndValues ...interface{}) {
	if defaultLogger != nil {
		args := addTraceID(keysAndValues...)
		defaultLogger.sugaredLogger.Debugw(msg, args...)
	}
}

// Info logs a message at InfoLevel with implicit trace_id
func Info(msg string, keysAndValues ...interface{}) {
	if defaultLogger != nil {
		args := addTraceID(keysAndValues...)
		defaultLogger.sugaredLogger.Infow(msg, args...)
	}
}

// Infof logs a formatted message at InfoLevel with implicit trace_id
func Infof(template string, args ...interface{}) {
	if defaultLogger != nil {
		msg := fmt.Sprintf(template, args...)
		traceID := GetTraceID()
		if traceID == "" {
			traceID = NewTraceID()
			SetTraceID(traceID)
		}
		defaultLogger.sugaredLogger.Infow(msg, traceIDKey, traceID)
	}
}

// Infow logs a message with structured fields at InfoLevel with implicit trace_id
func Infow(msg string, keysAndValues ...interface{}) {
	if defaultLogger != nil {
		args := addTraceID(keysAndValues...)
		defaultLogger.sugaredLogger.Infow(msg, args...)
	}
}

// Warn logs a message at WarnLevel with implicit trace_id
func Warn(msg string, keysAndValues ...interface{}) {
	if defaultLogger != nil {
		args := addTraceID(keysAndValues...)
		defaultLogger.sugaredLogger.Warnw(msg, args...)
	}
}

// Warnf logs a formatted message at WarnLevel with implicit trace_id
func Warnf(template string, args ...interface{}) {
	if defaultLogger != nil {
		msg := fmt.Sprintf(template, args...)
		traceID := GetTraceID()
		if traceID == "" {
			traceID = NewTraceID()
			SetTraceID(traceID)
		}
		defaultLogger.sugaredLogger.Warnw(msg, traceIDKey, traceID)
	}
}

// Warnw logs a message with structured fields at WarnLevel with implicit trace_id
func Warnw(msg string, keysAndValues ...interface{}) {
	if defaultLogger != nil {
		args := addTraceID(keysAndValues...)
		defaultLogger.sugaredLogger.Warnw(msg, args...)
	}
}

// Error logs a message at ErrorLevel with implicit trace_id
func Error(msg string, keysAndValues ...interface{}) {
	if defaultLogger != nil {
		args := addTraceID(keysAndValues...)
		defaultLogger.sugaredLogger.Errorw(msg, args...)
	}
}

// Errorf logs a formatted message at ErrorLevel with implicit trace_id
func Errorf(template string, args ...interface{}) {
	if defaultLogger != nil {
		msg := fmt.Sprintf(template, args...)
		traceID := GetTraceID()
		if traceID == "" {
			traceID = NewTraceID()
			SetTraceID(traceID)
		}
		defaultLogger.sugaredLogger.Errorw(msg, traceIDKey, traceID)
	}
}

// Errorw logs a message with structured fields at ErrorLevel with implicit trace_id
func Errorw(msg string, keysAndValues ...interface{}) {
	if defaultLogger != nil {
		args := addTraceID(keysAndValues...)
		defaultLogger.sugaredLogger.Errorw(msg, args...)
	}
}

// ==================== 实例方法 ====================

// Debug logs a message at DebugLevel with implicit trace_id
func (l *Logger) Debug(msg string, keysAndValues ...interface{}) {
	args := addTraceID(keysAndValues...)
	l.sugaredLogger.Debugw(msg, args...)
}

// Debugf logs a formatted message at DebugLevel with implicit trace_id
func (l *Logger) Debugf(template string, args ...interface{}) {
	msg := fmt.Sprintf(template, args...)
	traceID := GetTraceID()
	if traceID == "" {
		traceID = NewTraceID()
		SetTraceID(traceID)
	}
	l.sugaredLogger.Debugw(msg, traceIDKey, traceID)
}

// Debugw logs a message with structured fields at DebugLevel with implicit trace_id
func (l *Logger) Debugw(msg string, keysAndValues ...interface{}) {
	args := addTraceID(keysAndValues...)
	l.sugaredLogger.Debugw(msg, args...)
}

// Info logs a message at InfoLevel with implicit trace_id
func (l *Logger) Info(msg string, keysAndValues ...interface{}) {
	args := addTraceID(keysAndValues...)
	l.sugaredLogger.Infow(msg, args...)
}

// Infof logs a formatted message at InfoLevel with implicit trace_id
func (l *Logger) Infof(template string, args ...interface{}) {
	msg := fmt.Sprintf(template, args...)
	traceID := GetTraceID()
	if traceID == "" {
		traceID = NewTraceID()
		SetTraceID(traceID)
	}
	l.sugaredLogger.Infow(msg, traceIDKey, traceID)
}

// Infow logs a message with structured fields at InfoLevel with implicit trace_id
func (l *Logger) Infow(msg string, keysAndValues ...interface{}) {
	args := addTraceID(keysAndValues...)
	l.sugaredLogger.Infow(msg, args...)
}

// Warn logs a message at WarnLevel with implicit trace_id
func (l *Logger) Warn(msg string, keysAndValues ...interface{}) {
	args := addTraceID(keysAndValues...)
	l.sugaredLogger.Warnw(msg, args...)
}

// Warnf logs a formatted message at WarnLevel with implicit trace_id
func (l *Logger) Warnf(template string, args ...interface{}) {
	msg := fmt.Sprintf(template, args...)
	traceID := GetTraceID()
	if traceID == "" {
		traceID = NewTraceID()
		SetTraceID(traceID)
	}
	l.sugaredLogger.Warnw(msg, traceIDKey, traceID)
}

// Warnw logs a message with structured fields at WarnLevel with implicit trace_id
func (l *Logger) Warnw(msg string, keysAndValues ...interface{}) {
	args := addTraceID(keysAndValues...)
	l.sugaredLogger.Warnw(msg, args...)
}

// Error logs a message at ErrorLevel with implicit trace_id
func (l *Logger) Error(msg string, keysAndValues ...interface{}) {
	args := addTraceID(keysAndValues...)
	l.sugaredLogger.Errorw(msg, args...)
}

// Errorf logs a formatted message at ErrorLevel with implicit trace_id
func (l *Logger) Errorf(template string, args ...interface{}) {
	msg := fmt.Sprintf(template, args...)
	traceID := GetTraceID()
	if traceID == "" {
		traceID = NewTraceID()
		SetTraceID(traceID)
	}
	l.sugaredLogger.Errorw(msg, traceIDKey, traceID)
}

// Errorw logs a message with structured fields at ErrorLevel with implicit trace_id
func (l *Logger) Errorw(msg string, keysAndValues ...interface{}) {
	args := addTraceID(keysAndValues...)
	l.sugaredLogger.Errorw(msg, args...)
}

// Sync flushes any buffered log entries
func Sync() {
	if defaultLogger != nil {
		defaultLogger.sugaredLogger.Sync()
	}
}

func (l *Logger) Sync() error {
	return l.sugaredLogger.Sync()
}
